/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.wsonrpc.server;

import net.apexes.wsonrpc.core.WebSocketSession;
import net.apexes.wsonrpc.core.WsonrpcConfig;
import net.apexes.wsonrpc.core.WsonrpcEngine;
import net.apexes.wsonrpc.core.WsonrpcLogger;
import net.apexes.wsonrpc.json.JsonRpcError;
import net.apexes.wsonrpc.json.JsonRpcRequest;
import net.apexes.wsonrpc.json.JsonRpcResponse;
import net.apexes.wsonrpc.util.JsonRpcErrors;

import java.lang.reflect.Type;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
class WsonrpcServerEngine extends WsonrpcEngine {

    private WsonrpcRequestInterceptor wsonrpcRequestInterceptor;

    WsonrpcServerEngine(WsonrpcConfig config) {
        super(config);
    }

    void onError(String sessionId, Throwable error) {
        WsonrpcLogger logger = getConfig().getWsonrpcLogger();
        if (logger != null) {
            logger.onError(sessionId, error);
        }
    }

    void setWsonrpcRequestInterceptor(WsonrpcRequestInterceptor interceptor) {
        this.wsonrpcRequestInterceptor = interceptor;
    }

    @Override
    protected void handleRequest(WebSocketSession session, JsonRpcRequest request) {
        WsonrpcRequestInterceptor interceptor = wsonrpcRequestInterceptor;
        if (interceptor != null && request != null) {
            interceptor.handle(new ContextImpl(session, request));
        } else {
            execReply(session, request);
        }
    }

    private void execReply(WebSocketSession session, JsonRpcRequest request) {
        super.handleRequest(session, request);
    }

    @Override
    protected JsonRpcResponse execute(WebSocketSession session, JsonRpcRequest request) {
        if (WsonrpcSessions.get() == session) {
            return super.execute(session, request);
        } else {
            WsonrpcSessions.begin(session);
            try {
                return super.execute(session, request);
            } finally {
                WsonrpcSessions.end();
            }
        }
    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     */
    private class ContextImpl implements WsonrpcRequestInterceptor.Context {

        private final WebSocketSession session;
        private final JsonRpcRequest request;

        private ContextImpl(WebSocketSession session, JsonRpcRequest request) {
            this.session = session;
            this.request = request;
        }

        @Override
        public WebSocketSession getSession() {
            return session;
        }

        @Override
        public JsonRpcRequest getRequest() {
            return request;
        }

        @Override
        public String getServiceMethod() {
            return request.getMethod();
        }

        @Override
        public String getRequestId() {
            return request.getId();
        }

        @Override
        public Object getParams() {
            return request.getParams();
        }

        @Override
        public Object[] getParams(Type[] types) {
            return request.getParams(types);
        }

        @Override
        public String getParamsAsJson() {
            return request.getParamsAsJson();
        }

        @Override
        public boolean isNotification() {
            return request.getId() == null;
        }

        @Override
        public void executeReply() {
            execReply(session, request);
        }

        @Override
        public void replyValue(Object result) {
            if (request.getId() != null) {
                JsonRpcResponse resp = toResponse(request.getId(), result);
                transmit(session, resp);
            }
        }

        @Override
        public void replyThrowable(Throwable t) {
            replyError(JsonRpcErrors.internalError(t));
        }

        @Override
        public void replyError(JsonRpcError error) {
            if (request.getId() != null) {
                transmit(session, getJsonImplementor().createResponse(request.getId(), error));
            }
        }
    }
}
